#include "stdafx.h"
#include "Scanner.h"


CScanner::CScanner(CTokenManager & tokenManager)
    :m_currIndex(-1), m_currLineIndex(1), m_tokenManager(tokenManager)
{}

void CScanner::SetSourseCode(std::vector<char> const & sourceCode)
{
    m_bufferCode = sourceCode;
	Reset();
}

std::string CScanner::GetValue() const
{
    return m_value;
}

std::string CScanner::GetPrevValue() const
{
    return m_prevValue;
}

void CScanner::Reset()
{
	m_value = "";
	m_prevValue = "";
    m_currIndex = 0;
	m_currLineIndex = 1;
	m_lastTokens.clear();
}

vector<string> CScanner::GetLastTokens()
{
	return m_lastTokens;
}

string CScanner::GetTokenFromEnd(int offset)
{
	return m_lastTokens[m_lastTokens.size() - offset - 1];
}

CToken CScanner::GetNextToken()
{
	m_prevValue = m_value;
    while (true)
    {
        m_ch = GetChar();
		char peek = Peek();
        switch (m_ch)
        {
        case EOF:
            return CToken::EOF_TOKEN;
        case ' ':
            continue;
        case '\n':
			++m_currLineIndex;
            continue;
        case '\r':
			++m_currLineIndex;
            continue;
        case '\t':
            continue;
        case '{':
			m_lastTokens.push_back("{");
			return CToken::LFIGUREBRACKET;
        case '}':
			m_lastTokens.push_back("}");
			return CToken::RFIGUREBRACKET;
        case '(':
			m_lastTokens.push_back("(");
			return CToken::LROUNDBRACKET;
        case ')':
			m_lastTokens.push_back(")");
			return CToken::RROUNDBRACKET;
        case '[':
			m_lastTokens.push_back("[");
            return CToken::LSQUAREBRACKET;
        case ']':
			m_lastTokens.push_back("]");
            return CToken::RSQUAREBRACKET;
        case '.':
			m_lastTokens.push_back(".");
			return CToken::POINT;
        case ';':
			m_lastTokens.push_back(";");
			return CToken::SEMICOLON;
		case '!':
			m_lastTokens.push_back("!");
			return CToken::NOT;
		case '+':
			m_lastTokens.push_back("+");
			return CToken::ADD;
		case '-':
            if (peek >= '0' && peek <= '9')
            {
				return ReadNumber();
            }

			m_lastTokens.push_back("-");
			return CToken::SUB;
		case '*':
			m_lastTokens.push_back("*");
			return CToken::MUL;
		case '$':
			m_lastTokens.push_back("$");
			return CToken::DELIMETER;
		case ':':
			m_lastTokens.push_back(":");
			return CToken::COLON;
		case '/':
			m_lastTokens.push_back("/");
			return CToken::DIV;
		case ',':
			m_lastTokens.push_back(",");
			return CToken::COMMA;
		case '&':
			return IdentifyDoubleCharOperator('&', '&', CToken::AND);
		case '<':
			return IdentifyDoubleCharOperator('<', '=', CToken::LESSOREQUAL, CToken::LESS);
		case '|':
			return IdentifyDoubleCharOperator('|', '|', CToken::OR);
		case '=':
			return IdentifyDoubleCharOperator('=', '=', CToken::EQUAL, CToken::ASSIGN);
        default:
			m_value.push_back(m_ch);
            if (m_ch >= '0' && m_ch <= '9')
            {
                return ReadNumber();
            }
            else if (isalpha(m_ch))
            {
                return ReadKeyWordOrIdentifier();
            }
            else
            {
				stringstream errorMsg;
				errorMsg <<  "Error on line " << GetCurrLineIndex() << ". Unknown token!";
		
				throw domain_error(errorMsg.str());
            }
        }
    }
}

CToken CScanner::PeekToken()
{
	string tmpValue = m_value;
	int tmpCurrLineIndex = m_currLineIndex;
	int tmpCurrIndex = m_currIndex;

	CToken retVal;
	bool found = false;
    while (!found)
    {
        m_ch = GetChar();
		char peek = Peek();
        switch (m_ch)
        {
        case EOF:
			found = true;
            retVal = CToken::EOF_TOKEN;
			break;
        case ' ':
            continue;
        case '\n':
			++m_currLineIndex;
            continue;
        case '\r':
			++m_currLineIndex;
            continue;
        case '\t':
            continue;
        case '{':
			found = true;
			retVal = CToken::LFIGUREBRACKET;
			break;
        case '}':
			found = true;
			retVal = CToken::RFIGUREBRACKET;
			break;
        case '(':
			found = true;
			retVal = CToken::LROUNDBRACKET;
			break;
        case ')':
			found = true;
			retVal = CToken::RROUNDBRACKET;
			break;
        case '[':
			found = true;
            retVal = CToken::LSQUAREBRACKET;
			break;
        case ']':
			found = true;
            retVal = CToken::RSQUAREBRACKET;
			break;
        case '.':
			found = true;
			retVal = CToken::POINT;
			break;
        case ';':
			found = true;
			retVal = CToken::SEMICOLON;
			break;
		case '!':
			found = true;
			retVal = CToken::NOT;
			break;
		case '+':
			found = true;
			retVal = CToken::ADD;
			break;
		case '-':
            if (peek >= '0' && peek <= '9')
            {
				found = true;
                retVal = ReadNumber();
				break;
            }

			found = true;
			retVal = CToken::SUB;
			break;
		case '*':
			found = true;
			retVal = CToken::MUL;
			break;
		case '$':
			found = true;
			retVal = CToken::DELIMETER;
			break;
		case ':':
			found = true;
			retVal = CToken::COLON;
			break;
		case '/':
			found = true;
			retVal = CToken::DIV;
			break;
		case ',':
			found = true;
			retVal = CToken::COMMA;
			break;
		case '&':
			found = true;
			retVal = IdentifyDoubleCharOperator('&', '&', CToken::AND);
			break;
		case '<':
			found = true;
			retVal = IdentifyDoubleCharOperator('<', '=', CToken::LESSOREQUAL, CToken::LESS);
			break;
		case '|':
			found = true;
			retVal = IdentifyDoubleCharOperator('|', '|', CToken::OR);
			break;
		case '=':
			found = true;
			retVal = IdentifyDoubleCharOperator('=', '=', CToken::EQUAL, CToken::ASSIGN);
			break;
        default:
			m_value.push_back(m_ch);
            if (m_ch >= '0' && m_ch <= '9')
            {
				found = true;
                retVal = ReadNumber();
				break;
            }
            else if (isalpha(m_ch))
            {
				found = true;
                retVal = ReadKeyWordOrIdentifier();
				break;
            }
            else
            {
				stringstream errorMsg;
				errorMsg <<  "Error on line " << GetCurrLineIndex() << ". Unknown token!";
		
				throw domain_error(errorMsg.str());
            }
        }
    }

	m_value = tmpValue;
	m_currIndex = tmpCurrIndex;
	m_currLineIndex = tmpCurrLineIndex;

	return retVal;
}

CToken CScanner::IdentifyDoubleCharOperator(char firstHalfPperator, char secondHalfPperator, CToken expectedToken, CToken alternativeToken)
{
	if (Peek() == secondHalfPperator)
	{
		string newVal = "";
		newVal.push_back(firstHalfPperator);
		newVal.push_back(secondHalfPperator);
		m_lastTokens.push_back(newVal);
		m_currIndex++;
		return expectedToken;
	}
	else
	{
		if (alternativeToken == CToken::INCORRECT)
		{
			stringstream errorMsg;
			errorMsg <<  "Error on line " << GetCurrLineIndex() << ". Unknown token!";
		
			throw domain_error(errorMsg.str());
		}

		string newVal = "";
		newVal.push_back(firstHalfPperator);
		m_lastTokens.push_back(newVal);
		return alternativeToken;
	}
}

int CScanner::GetCurrLineIndex()
{
    return m_currLineIndex;
}

char CScanner::Peek() const
{
    return (m_currIndex >= (int)m_bufferCode.size()) ? EOF : m_bufferCode[m_currIndex];
}

char CScanner::GetChar()
{
    return (m_currIndex >= (int)m_bufferCode.size()) ? EOF : m_bufferCode[m_currIndex++];
}

CToken CScanner::ReadNumber()
{
    m_value = "";
    
	bool firstChar = true;

	while (m_ch != EOF && (isdigit(m_ch) || (firstChar && m_ch == '-')))
    {
		firstChar = false;
        m_value += m_ch;
        m_ch = GetChar();
    }
    
    if (m_ch != EOF)
        m_currIndex--;

	m_lastTokens.push_back(m_value);

	return CToken::INT_LITERAL;
}

CToken CScanner::ReadKeyWordOrIdentifier()
{
    m_value = "";
    while (m_ch != EOF && (isalpha(m_ch) || isdigit(m_ch)))
    {
        m_value += m_ch;
        m_ch = GetChar();
    }

	if (m_ch != EOF)
	{
		m_currIndex--;
	}

	m_lastTokens.push_back(m_value);

	return m_tokenManager.IsKeyWord(m_value) ? m_tokenManager.GetToken(m_value) : CToken::ID;
}